home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / aztcmtsk.arc / AZTASK.TSC < prev    next >
Text File  |  1986-03-24  |  10KB  |  359 lines

  1. /* ========================================================== */
  2. /*                                  */
  3. /*    TASKING.C -- task and queue management in AZTEC C     */
  4. /*                                  */
  5. /* See TASKING.DOC for description and explanation.  The      */
  6. /* interface declarations for this module, all that needs to  */
  7. /* be included in a client program, is in TASKING.H.  The     */
  8. /* internal contents of the Queue, Event, and Task structures */
  9. /* are visible only in this module with the code that uses    */
  10. /* them.                              */
  11. /*    Also included: a redefinition of the original AZTEC     */
  12. /* alloc(), with no improved function but with the dependecy  */
  13. /* on the hardware stack pointer removed.              */
  14. /*                                  */
  15. /* 3/1/86 coded                           */
  16. /* 3/13/86 corrected method of stacking arguments          */
  17. /*                                  */
  18. /* Copyright (C) 1986 by David E. Cortesi, 415 Cambridge Ave  */
  19. /* Suite 18, Palo Alto, CA 94306.  Permission to copy and     */
  20. /* redistribute without charge is granted provided only that  */
  21. /* this notice is retained.  Distribution for a fee, whether  */
  22. /* alone or as part of another product and whether or not for */
  23. /* profit, requires explicit permission from the author.      */
  24. /* ========================================================== */
  25. #include "a:stdio.h" /* for deadlock msg to stderr */
  26.  
  27. struct Queue {
  28.     int    g,    /* get-index of oldest value */
  29.         p,    /* put-index to next empty slot */
  30.         k,    /* max elements and highest index */
  31.         w;    /* nonzero if a task waits on this q */
  32.     unsigned *x;    /* ->k+1 words of storage */
  33.         } ;
  34.  
  35. struct Event {
  36.     struct Queue EQ;/* an event is a one-item queue */
  37.     int buff[2];    /* with its item-buffer appended */
  38.         };
  39.  
  40. struct Task    {
  41. /* forward-chain to the next task in list, or 0 if end          */
  42.     struct Task *nextask;
  43. /* 0 = active, 1 = sleeping, -1 = dead, ->queue if blocked    */
  44.     struct Queue *blocked;
  45. /* number of words in the allocated stack              */
  46.     int    stksize;
  47. /* saved CPU stack pointer (see #asm in defer() )          */
  48.     unsigned *stkptr;
  49. /* address of allocated stack array                  */
  50.     unsigned *stack;
  51.         };
  52.  
  53. /*
  54.  this Task has no function but to anchor the chain (in a
  55.  sense, it represents CROOT).  The word of 1 keeps it asleep
  56.  and it has no stack.
  57. */
  58. static struct Task TaskHead = { 0,1,0,0,0 };
  59.  
  60. /*
  61.  CurTask points to the present executor, after whom a
  62.  newly-made task is inserted.  Initializing to TaskHead
  63.  allows the first, main(), task, to be inserted.
  64. */
  65. static struct Task *CurTask = &TaskHead;
  66.  
  67. /*
  68.  TasCount is incremented in maketask(), decremented in
  69.  endtask() and killtask().  When it drops to 0, the program
  70.  ends with a call to exit().
  71. */
  72. static int TasCount = 0;
  73.  
  74. /* scratch words for scanning chain of tasks */
  75. static struct Task *xt, *yt;
  76. /* target of assembler save of stack pointer */
  77. static unsigned SAVESP;
  78.  
  79. /*
  80.  when qget() finds an empty queue or qput() finds a full one,
  81.  they call qwait() to put the current task to sleep on the
  82.  indicated queue.
  83. */
  84. /*void*/ qwait(adq) struct Queue *adq;
  85. {
  86.     CurTask->blocked = adq;
  87.     adq->w = CurTask;
  88.     defer();
  89. }
  90. /*
  91.  when qget() takes from or qput adds to a queue on which a
  92.  task is shown as waiting, they call qwake() to wake up any
  93.  and all tasks so waiting.
  94. */
  95. /*void*/ qwake(adq) register struct Queue *adq;
  96. {
  97.     for(
  98.         xt = TaskHead.nextask;
  99.         xt;
  100.         xt = xt->nextask )
  101.     {
  102.         if (xt->blocked == adq)
  103.             xt->blocked = 0;
  104.     }
  105.     adq->w = 0;
  106. }
  107. /*
  108.  return the current task's Task address
  109. */
  110. struct Task *myTask()
  111. { return(CurTask); }
  112. /*
  113.  inquire into some task's status.
  114. */
  115. int tactive(adt) register struct Task *adt;
  116. { return(adt->blocked == 0); }
  117. int tinact(adt) register struct Task *adt;
  118. { return(adt->blocked); }
  119. /*
  120.  To end a task we mark it with -1.  The next time defer()
  121.  sees it, it drops it from the chain.  The address of
  122.  endtask() is pushed on all stacks by maketask().
  123. */
  124. /*void*/ endtask()
  125. {
  126.     CurTask->blocked = -1; /* signal defer() to kill */
  127.     if (--TasCount > 0) defer();
  128.     else exit(0);
  129. }
  130. /*void*/ killtask(adt) register struct Task *adt;
  131. {
  132.     adt->blocked = -1;
  133.     if (--TasCount > 0) return;
  134.     exit(0);
  135. }
  136. /*
  137.  to create a task, we make a task block and chain it
  138.  just after the current task.
  139. */
  140. struct Task *maketask(fun,size,narg,arg0)
  141.     int (*fun)(), size, narg, arg0;
  142. {
  143. register struct Task *adt;
  144. unsigned *arg;
  145.  
  146. /*
  147.     following is for AZTEC 8-bit, with nonstandard alloc, no
  148.     sizeof, and no type-checking
  149. */
  150.     adt = alloc(/*sizeof(struct Task)*/10);
  151. /*
  152.     chain the new task in as immediate successor to the
  153.     currenttask, ahead of its siblings and after its parent.
  154. */
  155.     adt->nextask = CurTask->nextask;
  156.     CurTask->nextask = adt;
  157.     adt->blocked = 0;
  158.     adt->stksize = size;
  159.     ++TasCount;
  160. /*
  161.     set up the new task's stack by pushing onto it the given
  162.     arguments, then the address of endtask() in the position
  163.     of its caller's return address (so if it just returns,
  164.     it will end itself automatically), then its address so
  165.     that defer() will "return" to it.
  166. */
  167.     adt->stack = alloc(2*size);
  168.     adt->stkptr = adt->stack + size;
  169.     arg = (&arg0)+narg; /* 3/13/86 reproduce same stack.. */
  170.     for(;narg;--narg) /* 3/13/86 allow for narg==0 */
  171.         *--(adt->stkptr) = *--arg; /*3/13 ..sequence */
  172.     *--(adt->stkptr) = &endtask;
  173.     *--(adt->stkptr) = fun;
  174.     return(adt);
  175. }
  176. /*
  177.  suspend the active task and dispatch the next in sequence
  178. */
  179. /*void*/ defer()
  180. {
  181. /*
  182.     get the present hardware SP value, which points to
  183.     defer()'s return address in the stack area of the
  184.     currently-active task, and save it.
  185. */
  186.     &SAVESP; /* sets HL->SAVESP */
  187. #asm
  188.     xchg
  189.     lxi h,0
  190.     dad sp
  191.     xchg
  192.     mov m,e
  193.     inx h
  194.     mov m,d
  195. #endasm
  196.     CurTask->stkptr = SAVESP;
  197. /*
  198.     go hand-over-hand down the round-robin list looking
  199.     for a task that is ready to run.
  200. */
  201.     yt = CurTask;
  202.     for(;;)
  203.     {
  204.         xt = yt;
  205.         yt = xt->nextask;
  206.         if (yt == 0) /* *xt is last of chain, wrap */
  207.             yt = &TaskHead;
  208.         if (yt->blocked == 0) /* active task, stop */
  209.             break;
  210.         if (yt == CurTask) /* come all the way 'round */
  211.         {
  212.             fputs(
  213.      "\n\n*** IN DEADLOCK, ALL TASKS WAITING ***\n",
  214.                 stderr);
  215.             exit(16);
  216.         }
  217.         if (yt->blocked == -1) /* ended task, kill */
  218.         { /* remove from dispatch chain */
  219.             xt->nextask = yt->nextask;
  220.         /* here we would free the storage used by
  221.         yt->stack and yt->Task, if Aztec had free()*/
  222.             yt = xt;
  223.         }
  224.     }
  225. /*
  226.     dispatch the active task we found by putting its stack
  227.     pointer value in the hardware SP and exiting.
  228. */
  229.     CurTask = yt;
  230.     SAVESP = CurTask->stkptr;
  231.     SAVESP; /* gets HL=SAVESP */
  232. #asm
  233.     sphl
  234. #endasm
  235. }
  236. /*
  237.  test a queue in various ways
  238. */
  239. int qempty(adq) register struct Queue *adq;
  240. { return (adq->g == adq->p); }
  241. int qnotmt(adq) register struct Queue *adq;
  242. { return (adq->g != adq->p); }
  243. int qfull(adq) register struct Queue *adq;
  244. {
  245. register int np;
  246.     np = adq->p + 1;
  247.     if (np > adq->k) np = 0;
  248.     return(np == adq->g);
  249. }
  250. int qnotfull(adq) register struct Queue *adq;
  251. {
  252. register int np;
  253.     np = adq->p + 1;
  254.     if (np > adq->k) np = 0;
  255.     return(np != adq->g);
  256. }
  257. /*
  258.  make a queue to some size.  the array of fifo data is
  259.  allocated separately to the Queue structure.  one more
  260.  word must be allocated to this array than the max number
  261.  of items to be queued.
  262. */
  263. struct Queue *qmake(size) int size;
  264. {
  265. register struct Queue *adq;
  266. /* the following is for 8-bit AZTEC C, which lacks sizeof()
  267.    and a standard alloc, also lacks strict type-checks */
  268.     adq = alloc(/*sizeof(struct Queue)*/ 10);
  269.     adq->g = 0;
  270.     adq->p = 0;
  271.     adq->w = 0;
  272.     adq->k = size;
  273.     adq->x = alloc(size+size+2);  /* allow k+1 unsigneds */
  274.     return(adq);
  275. }
  276. /*
  277.  make an Event, a 1-item queue, with the 2-word fifo buffer
  278.  contained in its structure.
  279. */
  280. struct Event *evmake()
  281. {
  282. register struct Event *adq;
  283. /* the following is for 8-bit AZTEC C, which lacks sizeof()
  284.    and a standard alloc, also lacks strict type-checks */
  285.     adq = alloc(/*sizeof(struct Event)*/ 14);
  286.     adq->EQ.g = 0;
  287.     adq->EQ.p = 0;
  288.     adq->EQ.w = 0;
  289.     adq->EQ.k = 2;
  290.     adq->EQ.x = &(adq->buff);
  291.     return(adq);
  292. }
  293. /*
  294.  get the oldest item from a queue, blocking the current task
  295.  if the queue is empty, and unblocking any tasks that might
  296.  have been blocked in a qput call.
  297. */
  298. unsigned qget(adq) register struct Queue *adq;
  299. {
  300. unsigned val;
  301.     defer();
  302.     while (adq->g == adq->p) qwait(adq);
  303.     val = adq->x[adq->g++];
  304.     if (adq->g > adq->k) adq->g = 0;
  305.     if (adq->w) qwake(adq);
  306.     return(val);
  307. }
  308. /*
  309.  add an item to a queue, blocking the current task if the queue
  310.  is full, and unblocking any tasks that might have been blocked
  311.  in a qget call.
  312. */
  313. /*void*/ qput(adq,val)
  314.     register struct Queue *adq;
  315.     unsigned val;
  316. {
  317. register int np;
  318.     for(;;)
  319.     {
  320.         np = adq->p + 1;
  321.         if (np > adq->k) np = 0;
  322.         if (adq->g != np) break;
  323.         qwait(adq);
  324.     }
  325.     adq->x[adq->p] = val;
  326.     adq->p = np;
  327.     if (adq->w) qwake(adq);
  328. }
  329. /*
  330.  The following version of alloc() replaces the original in
  331.  AZTEC C v 1.05, which used the hardware stack pointer as the
  332.  upper limit to storage.  In a task, which has a small stack
  333.  area that was itself allocated from the heap, this is not the
  334.  thing to do.  Here we assume that on first entry, we were
  335.  called from maketask() which was called from croot(), and so
  336.  the hardware SP is in fact the top of storage.  We capture
  337.  this value for later use, and never test the SP thereafter.
  338. */
  339. char *alloc(size) unsigned size;
  340. {
  341. static char *corebase = 0, *coremax = 0;
  342. register char *cp;
  343.  
  344.     if (corebase == 0) /* first call, from croot() */
  345.     {
  346.         corebase = settop(0); /* capture $MEMRY */
  347.         coremax = (char *)(&size) - 1024; /* ..and SP */
  348.     }
  349.     cp = corebase; /* presumed allocation point */
  350.     if (((corebase += size) < cp) /* wraparound */
  351.      || (corebase > coremax) )    /* overflow */
  352.     {
  353.         corebase = cp;    /* recover */
  354.         return(0);    /* signal failure */
  355.     }
  356.     else return(cp);
  357. }
  358.  
  359.